package com.blockmeta.bbs.businesslibrary.widget.chartview;


import android.os.Handler;
import android.os.Message;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.LogUtils;

import com.blockmeta.bbs.businesslibrary.business.DataManager;
import com.blockmeta.bbs.businesslibrary.pojo.websocket.GetChartPOJO;
import com.blockmeta.bbs.businesslibrary.pojo.websocket.GetDepthPOJO;
import com.blockmeta.bbs.businesslibrary.websocket.AppDefine;
import com.blockmeta.bbs.businesslibrary.websocket.AppDefine.MergeMode;
import com.blockmeta.bbs.businesslibrary.websocket.ConnectCenter;
import com.blockmeta.bbs.businesslibrary.websocket.ConnectManager;
import com.blockmeta.bbs.businesslibrary.websocket.QuoteCenter;
import com.blockmeta.bbs.businesslibrary.websocket.Symbol;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import buf.Kline;
import buf.Return;
import buf.Trade;


public class ChartData
{
	public static final int MIN_MILLISECONDS = 60 * 1000;
	public static final int MIN5_MILLISECONDS = 60 * 5 * 1000;
	public static final int MIN15_MILLISECONDS = 60 * 15 * 1000;
	public static final int MIN30_MILLISECONDS = 60 * 30 * 1000;
	public static final int HOUR_MILLISECONDS = 60 * 60 * 1000;
	public static final int HOUR4_MILLISECONDS = 60 * 60 * 4 * 1000;
	public static final int DAY_MILLISECONDS = 60 * 60 * 24 * 1000;
	public static final int WHAT_NOTRADEDRAW = 1;
	protected int m_nCtype; // Chart種類，1(走勢圖)、2(K線)
	protected ChartPeroid m_enChartPeroid;
	protected ArrayList<CandleData> m_arCandles;
	protected Symbol m_symbol;
	protected WeakReference<IChartEventListener> m_listener;

	public static final String OPENSTR = "open";
	public static final String CLOSESTR = "close";
	public static final String COUNTSTR = "count";
	private int m_nCount; // for night trade symbol

	private List<Integer> m_listSession;
	private List<Map<String, Integer>> m_mapSession;
	private int m_minOpen, m_firstOpen;
	private long m_noTradeTime = 0L;
	protected Handler m_handler = new ChatDataHandler();

	int m_periodSecond = DAY_MILLISECONDS;


	/**
	 * 缩放相关
	 */
	public int m_dataIndex;//数据移动标示
	public int m_oldIndex;
	public float m_dataIndexOffset;
	public boolean m_isLoadAllData;
	public int m_networkRequest;
	public boolean m_nomoredata;
	private static final int FORWARD_DIRECTION = 1;
	private static final int BACKWARD_DIRECTION = 0;


	public ChartData(Symbol symbol, ChartPeroid type)
	{
		m_listener = new WeakReference<IChartEventListener>(null);
		m_symbol = symbol;
		m_enChartPeroid = type;
		if (m_enChartPeroid == ChartPeroid.CP_DC)
		{
			m_nCtype = 1;
		}
		else
		{
			m_nCtype = 2;
		}
		m_arCandles = new ArrayList<CandleData>();
		m_listSession = new ArrayList<Integer>();
		m_mapSession = new ArrayList<Map<String, Integer>>();
		m_periodSecond = getPeriodSecond();
	}



	public ChartData(ChartData chart)
	{
		m_enChartPeroid = chart.getChartPeroid();
		m_symbol = chart.getSymbol();
		m_arCandles = new ArrayList<CandleData>();
		m_listSession = new ArrayList<Integer>(chart.getSession());
		m_mapSession = new ArrayList<Map<String, Integer>>(chart.getSessionSection());
		m_minOpen = chart.m_minOpen;
		m_firstOpen = chart.m_firstOpen;

		m_nCount = chart.getCount();
		m_dataIndex = chart.m_dataIndex;


		m_dataIndexOffset = chart.m_dataIndexOffset;
		List<CandleData> tCandlData = new ArrayList<CandleData>(chart.getCandleData());

		for (CandleData candle : tCandlData)
		{
			if (candle != null)
			{
				m_arCandles.add(candle.clone());
			}
		}
	}

	public ChartData(ChartPeroid pType)
	{
		m_enChartPeroid = pType;
		m_arCandles = new ArrayList<CandleData>();
		m_listSession = new ArrayList<Integer>();
		m_mapSession = new ArrayList<Map<String, Integer>>();
	}


	private ConnectManager getConnectManager()
	{
		return ConnectCenter.getConnectCenter().getConnectManager(m_symbol.getMarket());
	}

	public List<Integer> getSession()
	{
		return m_listSession;
	}

	public List<Map<String, Integer>> getSessionSection()
	{
		return m_mapSession;
	}

	public void clear()
	{
		m_arCandles.clear();
		m_listSession.clear();
		m_mapSession.clear();
		m_minOpen = 0;
		m_firstOpen = 0;
	}

	public void setEventListener(String key, IChartEventListener listener)
	{
		if (key != null && listener == null )
		{
			return ;
		}

		m_listener = new WeakReference<IChartEventListener>(listener);

		if (listener != null)
		{
			postCache();
		}
	}

	public IChartEventListener getEventListener()
	{
		return m_listener.get();
	}


	protected void postCache()
	{
		if (m_arCandles.size() > 0)
		{
			IChartEventListener listener = m_listener.get();
			if (listener != null)
			{
				m_dataIndexOffset = m_arCandles.size() - listener.getDisplayDataCount();
				m_dataIndex = m_dataIndexOffset < 0 ? 0 : (int) m_dataIndexOffset;
				listener.onRefresh(this, AppDefine.UpdateMode.REFRESH);
			}
		}
	}

	protected boolean isMinuteChart()
	{
		return !(m_enChartPeroid == ChartPeroid.CP_1week || m_enChartPeroid == ChartPeroid.CP_1month
		);
	}

	public void query(String key, ChartData.ChartPeroid freq)
	{

		if(freq == ChartPeroid.DEPTH)
		{
			getDepth(key);
		}
		else
		{
			getChart2(key);
		}

	}



	/**
	 * 為了不影響舊版本的運作, 新增這個 api 去拿取有夜盤的商品
	 */
	void subscribe2(String key)
	{

	}

	void getChart2(String key)
	{
		getChart2(key,BACKWARD_DIRECTION,0);
	}

	void getChart2(String key,int pDirection,long pDate)
	{

		ConnectManager connectManager = getConnectManager();
		if(connectManager == null)
			return;

		GetChartPOJO pkt = new GetChartPOJO();
		pkt.set_pt("get_kline_list");
		pkt.setExchange(m_symbol.getExchange());
		pkt.setSymbol(m_symbol.getSymbolCode());
		pkt.setTime_type(m_enChartPeroid.getValue());
		pkt.set_seq(ConnectManager.getSeqNo());
		pkt.setCount(200);
		if(pDirection == BACKWARD_DIRECTION)
		{
			if(CoinChart.PERIODQUERY.equals(key) && m_arCandles.size() > 0)
			{
				if(m_arCandles.size() > 1) {//如果有两根的可以去获取以最后两个的时间，只有一根就获取以最后一个
					pkt.setStart_time((int) (m_arCandles.get(m_arCandles.size() - 2).getDate() / 1000));
				}
				else
				{
					pkt.setStart_time((int) (m_arCandles.get(m_arCandles.size() - 1).getDate() / 1000));
				}
			}
		}
		else
		{
			pkt.setEnd_time((int)(pDate / 1000));
		}

		connectManager.write(pkt);
	}

	void getDepth(String key)
	{

		ConnectManager connectManager = getConnectManager();
		if(connectManager == null)
			return;

		GetDepthPOJO pkt = new GetDepthPOJO();
		pkt.set_pt("subscribe_depth");
		pkt.setExchange(m_symbol.getExchange());
		pkt.setSymbol(m_symbol.getSymbolCode());
		pkt.set_seq(connectManager.getSeqNo());

		connectManager.write(pkt);
	}




	public void unregisterChart()
	{

	}

	public void unregisterChart(String key, ChartPeroid chartPeroid)
	{
		if(chartPeroid == ChartPeroid.DEPTH)
		{
			ConnectManager connectManager = getConnectManager();
			if(connectManager == null)
				return;

			GetDepthPOJO pkt = new GetDepthPOJO();
			pkt.set_pt("unsubscribe_depth");
			pkt.setExchange(m_symbol.getExchange());
			pkt.setSymbol(m_symbol.getSymbolCode());
			pkt.set_seq(ConnectManager.getSeqNo());

			connectManager.write(pkt);
		}
	}











	private synchronized void insertCandleData(List<CandleData> totalCandles)
	{
		if (totalCandles.size() > 0)
		{
			int index = m_arCandles.size() - 1;

			CandleData candle = totalCandles.get(0);
			long date = candle.m_nDate;

			for (index = m_arCandles.size() - 1; index >= 0; index--)
			{
				CandleData data = m_arCandles.get(index);

				if (data.m_nDate < date
				) // 找到前面一个真实数据
				{
					break;
				}
			}

			// 清除掉冗余数据
			m_arCandles.subList(index + 1, m_arCandles.size()).clear();

			m_arCandles.addAll(totalCandles);
		}
	}




	/**
	 *
	 * @param packet
	 */
	public synchronized void processChartUpdate(Return.NormalReturn packet)
	{
		if(packet == null || packet.getExtraMap() == null || packet.getKlineList() == null || packet.getExtraMap().get("start_time") == null) {
			return;
		}

		List<CandleData> tmpCandles = new ArrayList<>();

		Kline.KlineList klineList = packet.getKlineList();

		int size = klineList.getKlinesCount();

		for (int i = 0; i < size; i++)
		{
			Kline.KlineListItem item = klineList.getKlines(i);

			CandleData data = new CandleData(item);

			data.setDate(data.getDate() * 1000L);
			tmpCandles.add(data);
		}

		IChartEventListener listener = m_listener.get();

		int tbeforeCandleSize = m_arCandles.size();

		AppDefine.UpdateMode updateMode = AppDefine.UpdateMode.REFRESH;

		int tempListSize = tmpCandles.size();

		if(m_arCandles.size() > 0)
		{
			if("0".equals(packet.getExtraMap().get("start_time")) && !"0".equals(packet.getExtraMap().get("end_time")))//start_time没有传为0表示向过去取数据
			{
				m_networkRequest = 0;
				if(listener != null)
				{
					listener.showLoadingBar(false);
				}

				if(tempListSize == 0)//没有更多数据
				{
					m_nomoredata = true;
//					LogUtils.d( "m_nomoredata is true");
				}else
				{
					m_nomoredata = false;
					insertCandleDataInFront(tmpCandles);
//					LogUtils.d( "insertCandleDataInFront");
				}
			}
			else
			{
				updateCandleData(packet, tmpCandles);
			}
		}
		else
		{
			updateCandleData(packet, tmpCandles);
		}

		if(listener != null && getChartPeroid() != ChartPeroid.CP_DC)
		{
			int tAfterCandleSize = m_arCandles.size();

			if (tbeforeCandleSize == 0)//刚开始没有数据的情况还要再考虑
			{
				m_dataIndexOffset = tAfterCandleSize - listener.getDisplayDataCount();//用于不满一屏幕获取新数据
//				LogUtils.d("tingchart","processChart2Update m_arCandles.size() == 0 m_dataIndexOffset"+m_dataIndexOffset+"tmpCandles.size() "+tmpCandles.size() );
			}
			else
			{
				m_dataIndexOffset += (tAfterCandleSize - tbeforeCandleSize);
//				LogUtils.d("tingchart","processChart2Update m_dataIndexOffset"+m_dataIndexOffset+"tmpCandles.size() "+tmpCandles.size() );
			}
		}

		m_nCount = m_arCandles.size();

		notifyRefresh(updateMode,true);

		sendPeriodMessageForNoTradeDraw();
	}


	public synchronized void processDepthUpdate(Return.NormalReturn packet)
	{
		if(packet == null || packet.getExtraMap() == null || packet.getDepthData() == null) {
			return;
		}

		List<CandleData> tmpCandles = new ArrayList<>();

		buf.DepthOuterClass.Depth depth = packet.getDepthData();

		int askSize = depth.getAsksCount();
		int bidSize = depth.getBidsCount();

		float bidVolumn = 0.f;
		for (int i = 0; i < bidSize; i++)
		{
			buf.DepthOuterClass.DepthItem item = depth.getBids(i);
			bidVolumn += item.getVolume();
			CandleData data = new CandleData(item.getPrice(), bidVolumn);
			data.m_fake = true;
			tmpCandles.add(0, data);
		}
		float askVolumn = 0.f;
		for (int i = 0; i < askSize; i++)
		{
			buf.DepthOuterClass.DepthItem item = depth.getAsks(i);
			askVolumn += item.getVolume();
			CandleData data = new CandleData(item.getPrice(), askVolumn);
			data.m_fake = false;
			tmpCandles.add(data);
		}

		DataManager.getDataManager().getQuoteCenter().post(
				new QuoteCenter.Event_DeepQuotePercent(m_symbol, bidVolumn/(bidVolumn + askVolumn)));


		IChartEventListener listener = m_listener.get();

		int tbeforeCandleSize = m_arCandles.size();

		AppDefine.UpdateMode updateMode = AppDefine.UpdateMode.REFRESH;

		int tempListSize = tmpCandles.size();

		m_arCandles.clear();
		m_arCandles.addAll(tmpCandles);

		m_nCount = m_arCandles.size();

		if(listener != null) {
			listener.onRefresh(this, AppDefine.UpdateMode.REFRESH);
		}
	}

	private void updateCandleData(Return.NormalReturn packet, List<CandleData> tmpCandles)
	{
		insertCandleData(tmpCandles);


	}

	public void insertCandleDataInFront(List<CandleData> totalCandles)
	{
		if (totalCandles.size() > 0)
		{
			int index = m_arCandles.size() - 1;

			CandleData candle = totalCandles.get(totalCandles.size() - 1);//获得要插入的数据的最大时间
			long date = candle.m_nDate;

			for (index = 0; index < m_arCandles.size(); index++)
			{
				CandleData data = m_arCandles.get(index);

				if (data.m_nDate > date
				) // 如果原数据集中的最小时间大于要加入的数据集的最大时间
				{
					break;
				}
			}

			// 清除掉冗余数据
			m_arCandles.subList(0, index).clear();

			m_arCandles.addAll(0,totalCandles);
		}

	}

	private void notifyRefresh(AppDefine.UpdateMode updateMode, boolean needRefreshData)
	{

		int i = 0;
		IChartEventListener listener = m_listener.get();
		if(listener != null)
		{
//			LogUtils.d("notifyRefresh m_dataIndexOffset"+m_dataIndexOffset+" m_networkRequest"+m_networkRequest);
			if (m_dataIndexOffset <= 0)
			{
				m_dataIndex = 0;
				if (m_isLoadAllData)
				{//如果
					m_dataIndexOffset = 0;
				}
				if (m_networkRequest <= 0 && !m_isLoadAllData && !m_nomoredata)
				{//取前n个点的数据
					if (m_arCandles == null || m_arCandles.size() == 0)
					{
						//获取下面的数据
					}
					else
					{
						CandleData lastCandle = m_arCandles.get(0);
						if (lastCandle != null)
						{
							getChart2("", FORWARD_DIRECTION, lastCandle.m_nDate);
							m_networkRequest = 1;
							listener.showLoadingBar(true);
						}
					}
				}
			}
			else
			{

				int displayDataCount = m_arCandles.size() - listener.getDisplayDataCount();//如果向后滑动的时候超出了，就设为displayDataCount
				if (displayDataCount >= 0)
				{
					i = displayDataCount;
				}
				if (m_dataIndexOffset > i)
				{
					m_dataIndexOffset = i;
				}
				m_dataIndex = (int) m_dataIndexOffset;

			}
			if (needRefreshData)
			{
				listener.onRefresh(this, AppDefine.UpdateMode.REFRESH);//每次拖动，缩放获取新的数据后都去setdata一下
			}
			else
			{
				if (m_oldIndex != 0 && m_oldIndex == m_dataIndex)//没有变化时不重绘
				{
				}
				else
				{
					listener.reDrawChart(this, m_dataIndex, m_dataIndexOffset);
				}
				m_oldIndex = m_dataIndex;
			}
		}
	}

	private static Pattern SessionPattern = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2})-(\\d{2}):(\\d{2}):(\\d{2})");

	private void initSessionList(String session)
	{
		// session = "09:30:00-11:00:30,12:00:00-12:30:45,14:30:50-15:00:30";
		List<Map<String, Integer>> tMapSession = new ArrayList<Map<String,Integer>>();
		List<Integer> tListSesson = new ArrayList<Integer>();

		// 最小开盘时间
		int minOpen = Integer.MAX_VALUE;
		int firstOpen = 0;

		Matcher matcher = SessionPattern.matcher(session);
		while (matcher.find())
		{
			int open = getSecond(matcher.group(1), matcher.group(2), matcher.group(3));
			int close = getSecond(matcher.group(4), matcher.group(5), matcher.group(6));
			int count = getMinuteDiff(open, close) + 1;

			minOpen = Math.min(minOpen, open);
			if (firstOpen == 0)
			{
				firstOpen = open;
			}

			HashMap<String, Integer> tMap = new HashMap<String, Integer>();
			tMap.put(ChartData.OPENSTR, open);
			tMap.put(ChartData.CLOSESTR, close);
			tMap.put(ChartData.COUNTSTR, count);
			tMapSession.add(tMap);

			for (int jj = 0; jj < count; jj++)
			{
				tListSesson.add(addMinute(open, jj));
			}
		}

		m_minOpen = minOpen;
		m_firstOpen = firstOpen;
		m_mapSession = tMapSession;
		m_listSession = tListSesson;
	}

	/**
	 *
	 * @param time 093045
	 * @param diff >= 0
	 * @return
	 */
	private static int addMinute(int time, int diff)
	{
		int hour = time / 10000;
		int minute = time / 100 % 100;

		int diffHour = diff / 60;
		int diffMinute = diff % 60;

		minute += diffMinute;
		hour += diffHour;

		if (minute >= 60)
		{
			hour += 1;
			minute -= 60;
		}

		if (hour >= 24)
		{
			hour -= 24;
		}

		time = hour * 10000 + minute * 100 + (time % 100);

		return time;
	}

	/**
	 *
	 * @param time 093045
	 * @param diff >= 0
	 * @return
	 */
	private static int minusMinute(int time, int diff)
	{
		int hour = time / 10000;
		int minute = time / 100 % 100;

		int diffHour = diff / 60;
		int diffMinute = diff % 60;

		minute -= diffMinute;
		hour -= diffHour;

		if (minute < 0)
		{
			hour -= 1;
			minute += 60;
		}

		if (hour < 0)
		{
			hour += 24;
		}

		time = hour * 10000 + minute * 100 + (time % 100);

		return time;
	}

	private static int getMinuteDiff(int open, int close)
	{
		int minute1 = open / 10000 * 60 + (open / 100 % 100);
		int minute2 = close / 10000 * 60 + (close / 100 % 100);

		int minute = minute2 - minute1;
		if (minute < 0)
		{
			minute += 1440; // 如果小於 0 表示 session 的時間有跨夜, 所以要加上 1440 根
		}

		return minute;
	}

	private static int getSecond(String hour, String minute, String second)
	{
		int value = Integer.parseInt(hour);

		value *= 100;

		value += Integer.parseInt(minute);

		value *= 100;

		value += Integer.parseInt(second);

		return value;
	}


	public Symbol getSymbol()
	{
		return m_symbol;
	}

	public int getCount()
	{
		return m_nCount;
	}

	public ArrayList<CandleData> getCandleData()
	{
		return m_arCandles;
	}

	public CandleData getLastCandle()
	{
		if (m_arCandles.size() > 0) {
			return m_arCandles.get(m_arCandles.size() - 1);
		}
		return null;
	}

	public ChartPeroid getChartPeroid()
	{
		return m_enChartPeroid;
	}

	public void processTradeUpdate(Return.NormalReturn packet) {
		if (m_listener.get() == null) {
			return;
		}
		IChartEventListener listener = m_listener.get();
		if(!isMinuteChart()){
			return;
		}
		CandleData lastCandle = getLastCandle();
		if(lastCandle == null){
			return;
		}
		LogUtils.d("tingchart", "processTradeUpdate m_enChartPeroid" + m_enChartPeroid +" lastCandle time"+ lastCandle.getDate());
		Trade.TradeList tradeList = packet.getTradeList();
		if(tradeList == null){
			return;
		}
		List<Trade.TradeListItem> tradesList = tradeList.getTradesList();
		if(tradeList == null || tradesList.size() == 0){
			return;
		}
		for(Trade.TradeListItem tradeListItem : tradesList)
		{
			double price = tradeListItem.getPrice();
			float volumn = tradeListItem.getVolume();
			long tradeDate = tradeListItem.getDate() * 1000L;
//			String type = tradeListItem.getType();
			MergeMode enMergeMode= checkMergeMode(lastCandle, tradeDate);
			if (enMergeMode == MergeMode.APPEND)
			{
				long curCandleDate = getNextCandleDate(lastCandle, tradeDate);
				CandleData candleNeedAppend = new CandleData(curCandleDate, price, volumn);

				candleNeedAppend.setDbOpen(price);
				candleNeedAppend.setDbHigh(price);
				candleNeedAppend.setDbLow(price);
//				LogUtils.d("tingchart","candleNeedAppend curCandleDate" + curCandleDate + " high:"+candleNeedAppend.getDbHigh()+",open:"+candleNeedAppend.getDbOpen()+",low:"+candleNeedAppend.getDbLow());

				LogUtils.d("tingchart", "beforeadd m_enChartPeroid" + m_enChartPeroid +" size" + m_arCandles.size() + "lastCandle time"+ lastCandle.getDate());
				m_arCandles.add(candleNeedAppend);
				m_dataIndex++;
				lastCandle = getLastCandle();
				LogUtils.d("tingchart", "afteradd size" + m_arCandles.size() + "lastCandle time"+ lastCandle.getDate());
				if (listener != null)
				{
					listener.onRefresh(this, AppDefine.UpdateMode.INSERT);
				}
			}
			else if (enMergeMode == MergeMode.UPDATE){

				lastCandle.updateHighLowClose(price);
				lastCandle.m_lVolume += volumn;
				LogUtils.d("tingchart","MergeMode.UPDATE high:"+lastCandle.getDbHigh()+",open:"+lastCandle.getDbOpen()+",low:"+lastCandle.getDbLow());
				if (listener != null)
				{
					listener.onRefresh(this, AppDefine.UpdateMode.UPDATE);
				}
			}
		}
		sendPeriodMessageForNoTradeDraw();
	}

	protected void removeHandlerMessage() {
		m_handler.removeCallbacksAndMessages(null);
	}


	protected void sendPeriodMessageForNoTradeDraw() {

		Thread t = Thread.currentThread();
		LogUtils.d("tingchart", "sendPeriodMessageForNoTradeDraw" + t.getName() + " "+ t.getId());
		Message msg = m_handler.obtainMessage(WHAT_NOTRADEDRAW);
		m_handler.removeMessages(WHAT_NOTRADEDRAW);
		m_handler.sendMessageDelayed(msg, m_periodSecond);
	}


	private long getNextCandleDate(CandleData lastCandle, long tradeDate) {
		long nextCandleDate= lastCandle.getDate();
		switch (m_enChartPeroid) {
			case CP_1min:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, MIN_MILLISECONDS);
				break;
			case CP_5min:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, MIN5_MILLISECONDS);
				break;
			case CP_15min:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, MIN15_MILLISECONDS);
				break;
			case CP_30min:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, MIN30_MILLISECONDS);
				break;
			case CP_1hour:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, HOUR_MILLISECONDS);
				break;
			case CP_4hour:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, HOUR4_MILLISECONDS);
				break;
			case CP_1day:
				nextCandleDate = getTradeCandleDate(lastCandle, tradeDate, DAY_MILLISECONDS);
				break;
			default:
				break;
		}
		return nextCandleDate;
	}

	private long getTradeCandleDate(CandleData lastCandle, long tradeDate, long period) {
		long nextCandleDate= lastCandle.getDate();
		do {
			nextCandleDate += period;
		}while (tradeDate - nextCandleDate > period);
//		LogUtils.d("tingchart","getTradeCandleDate nextCandleDate"+ nextCandleDate);
		return nextCandleDate;
	}

	protected MergeMode checkMergeMode(CandleData lastCandle, long date) {
		MergeMode enMergeMode = MergeMode.IGNORE;
		long lastCandleDateTime = lastCandle.getDate();
		if (lastCandleDateTime > 0 && date >= lastCandleDateTime) {
			long diff = date - lastCandleDateTime;
			m_noTradeTime = diff;
//			LogUtils.d("tingchart", "checkMergeMode" + lastCandle.m_nDate + " date" + date + "diff" + diff);
			switch (m_enChartPeroid) {
				case CP_1min:
					enMergeMode = getMergeMode(diff, MIN_MILLISECONDS);
					break;
				case CP_5min:
					enMergeMode = getMergeMode(diff, MIN5_MILLISECONDS);
					break;
				case CP_15min:
					enMergeMode = getMergeMode(diff, MIN15_MILLISECONDS);
					break;
				case CP_30min:
					enMergeMode = getMergeMode(diff, MIN30_MILLISECONDS);
					break;
				case CP_1hour:
					enMergeMode = getMergeMode(diff, HOUR_MILLISECONDS);
					break;
				case CP_4hour:
					enMergeMode = getMergeMode(diff, HOUR4_MILLISECONDS);
					break;
				case CP_1day:
					enMergeMode = getMergeMode(diff, DAY_MILLISECONDS);
					break;
				default:
					break;
			}
		}
		return enMergeMode;
	}

	private int getPeriodSecond() {
		int periodTime = DAY_MILLISECONDS;
		switch (m_enChartPeroid) {
			case CP_1min:
				periodTime = MIN_MILLISECONDS;
				break;
			case CP_5min:
				periodTime = MIN5_MILLISECONDS;
				break;
			case CP_15min:
				periodTime = MIN15_MILLISECONDS;
				break;
			case CP_30min:
				periodTime = MIN30_MILLISECONDS;
				break;
			case CP_1hour:
				periodTime = HOUR_MILLISECONDS;
				break;
			case CP_4hour:
				periodTime = HOUR4_MILLISECONDS;
				break;
			case CP_1day:
				periodTime = DAY_MILLISECONDS;
				break;
			default:
				break;
		}
		return periodTime;
	}

	public class ChatDataHandler extends Handler {

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case WHAT_NOTRADEDRAW:
					if (m_listener.get() == null) {
						return;
					}
					drawNoTradeBar();
					sendPeriodMessageForNoTradeDraw();
					break;
			}
		}
	}

	private void drawNoTradeBar() {
		IChartEventListener listener = m_listener.get();
		CandleData lastCandle = getLastCandle();
		long curCandleDate = getNextCandleDate(lastCandle, lastCandle.getDate());
		CandleData candleNeedAppend = new CandleData(curCandleDate, lastCandle.getDbClose(), 0f);

		candleNeedAppend.setDbOpen(lastCandle.getDbClose());
		candleNeedAppend.setDbHigh(lastCandle.getDbClose());
		candleNeedAppend.setDbLow(lastCandle.getDbClose());

		LogUtils.d("tingchart", "WHAT_NOTRADEDRAW beforeadd m_enChartPeroid" + m_enChartPeroid +" size" + m_arCandles.size() + "lastCandle time"+ lastCandle.getDate());
		m_arCandles.add(candleNeedAppend);
		m_dataIndex++;
		LogUtils.d("tingchart", "WHAT_NOTRADEDRAW afteradd size" + m_arCandles.size() + "lastCandle time"+ lastCandle.getDate());
		if (listener != null)
		{
			listener.onRefresh(this, AppDefine.UpdateMode.INSERT);
		}
	}

	@NonNull
	private MergeMode getMergeMode(long diff, int period) {
		if (diff < period) {
			return MergeMode.UPDATE;
		} else {
			return MergeMode.APPEND;
		}
	}

	public static class ExrightFactor{
		private int date;
		private String description;
		private double factor;

		public ExrightFactor() {}

		public ExrightFactor(int date, String description, double factor) {
			this.date = date;
			this.description = description;
			this.factor = factor;
		}

		public int getDate() {
			return date;
		}

		public void setDate(int date) {
			this.date = date;
		}

		public String getDescription() {
			return description;
		}

		public void setDescription(String description) {
			this.description = description;
		}

		public double getFactor() {
			return factor;
		}

		public void setFactor(double factor) {
			this.factor = factor;
		}

		@Override
		public boolean equals(Object obj) {

			ExrightFactor  target=(ExrightFactor)obj;
			if(this.getDate()==((ExrightFactor) obj).getDate()) return true;
			return super.equals(obj);
		}

		@Override
		public String toString() {
			return "["+"date:"+date+",factor"+factor+",description:"+description+"]";
		}
	}

	public  enum ChartExemp
	{
		CE_NONE, CE_BEFORE, CE_AFTER;

		public static ChartExemp parse(int index){
			switch (index){
				case 0:
					return CE_NONE;
				case 1:
					return CE_BEFORE;
				case 2:
					return CE_AFTER;
			}

			return CE_NONE;
		}
	}

	public enum ChartPeroid
	{
		CP_DC("DC"),
		PERFORMANCE("PERFORMANCE"),
		DEPTH("DEPTH"),
		CP_1min("1min"),
		CP_5min("5min"),
		CP_15min("15min"),
		CP_30min("30min"),
		CP_1hour("1hour"),
		CP_4hour("4hour"),
		CP_1day("1day"),
		CP_1week("1week"),
		CP_1month("1month");

		static private HashMap<String, ChartPeroid> m_map = new HashMap<String, ChartPeroid>();
		static
		{
			for (ChartPeroid status: ChartPeroid.values())
			{
				m_map.put(status.getValue(), status);
			}
		}

		protected String m_value;

		ChartPeroid(String value)
		{
			m_value = value;
		}

		public String getValue()
		{
			return m_value;
		}

		public int getQueryCount()
		{
			return  160;
		}

		public boolean isEqual(ChartPeroid type)
		{
			return m_value.equals(type.getValue());
		}


		/**
		 *
		 * @param type
		 * @return
		 */
		public static ChartPeroid getChartType(String type)
		{
			ChartPeroid tmp = m_map.get(type);
			if (tmp == null)
			{
				tmp = CP_15min;
			}

			return tmp;
		}
	}


	public interface IChartEventListener
	{
		void onRefresh(ChartData chart, AppDefine.UpdateMode mode);

		void reDrawChart(ChartData chart, int pdataIndex, float pdataIndexOffset);//为了防止在移动过程中获取到数据影响了index，所以传过来

		int getDisplayDataCount();

		void showLoadingBar(boolean pIsShow);
	}

	/**
	 * 移动数据浮标，
	 * @param offset  <0 为显示之前的数据 >0显示后面的数据
	 */
	public void moveIndex(String key,int offset) {
		boolean canMove = false;//m_dataIndexOffset < 0 ? offset > 0 : (m_dataIndexOffset+offset) > 0;

		if(m_networkRequest > 0 && m_dataIndexOffset <= 0 && offset > 0)
		{
			canMove = true;
			m_dataIndexOffset = 0;
//			LogUtils.d( "set m_dataIndexOffset 0"+" m_networkRequest"+m_networkRequest);
		}
		else if(m_networkRequest == 0 && m_nomoredata && m_dataIndexOffset < 0)
		{
			canMove = true;
			m_dataIndexOffset = 0;
		}
		else
		{
			canMove = (m_dataIndexOffset + offset) > 0;
		}
		if(m_networkRequest <= 0 || canMove)//如果没有还在执行的网络请求或者m_dataIndexOffset＋offset > 0
		{
//			LogUtils.d( "moveIndex" + offset);
			m_dataIndexOffset += offset;
//			LogUtils.d("author"+ "m_dataIndexOffset" + m_dataIndexOffset);
			notifyRefresh(AppDefine.UpdateMode.REFRESH, false);
		}
	}

	/**
	 * 放大操作
	 * @param oldDisplayCount
	 * @param newDisplayCount
	 * @param pzoomRate  和用户放大的位置有关，如果在前端会小于0.5
	 */
	public void zoom(String key,int oldDisplayCount, int newDisplayCount,float pzoomRate) {
		boolean canMove = false;
		float tOffset = ((oldDisplayCount - newDisplayCount) * pzoomRate);
		if(m_networkRequest > 0 && m_dataIndexOffset <= 0 && tOffset > 0)
		{
			canMove = true;
			m_dataIndexOffset = 0;
		}else
		{
			canMove = (m_dataIndexOffset + tOffset) > 0;
		}
		if(m_networkRequest <= 0 || canMove)
		{
//			FDTLogUtils.d("tingchart", "zoom" + oldDisplayCount + " " + newDisplayCount + " offset" + (int) tOffset);

			m_dataIndexOffset += tOffset;
			notifyRefresh(AppDefine.UpdateMode.REFRESH, false);
		}
	}

}
